Skip to content

perf(observability): opt-in DAP trace + proxy logger honors effective log level - #410

Merged
debugmcpdev merged 1 commit into
mainfrom
perf/403-trace-gating-and-log-level
Aug 22, 2026
Merged

perf(observability): opt-in DAP trace + proxy logger honors effective log level#410
debugmcpdev merged 1 commit into
mainfrom
perf/403-trace-gating-and-log-level

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Fixes #403

Two always-on observability defaults in the proxy worker were heavyweight for every session (evidence in the issue: a 658 MB dev logs/ directory):

1. Per-frame DAP trace is now opt-in

The default trace-file factory set DAP_TRACE_FILE for every session, and MinimalDapClient then appendFileSync'd every frame in both directions — synchronous I/O on the proxy's event loop in the hot path of every step/variables/stackTrace exchange, into an uncapped ndjson file.

Now:

  • Default off. DAP_TRACE=1 (or true) enables the standard per-session dap-trace-<sessionId>.ndjson; an explicit DAP_TRACE_FILE (inherited via the spawn env) is honored as-is, without renaming.
  • 50 MB cap when enabled (consistent with the main logger's maxsize): one truncation marker line, then silence for the client's lifetime. Cap injectable for tests (traceMaxBytes).
  • The hooks.createTraceFile test seam is unchanged.
  • Sync writes retained deliberately (issue proposed a buffered async queue): tracing is now an opt-in crash-triage tool, and a write queue loses the final frames exactly when the process dies — the moment you need them. Happy to split this out if the sync-write latency matters to someone who has tracing on.

2. Proxy logger honors the effective log level

proxy-<sessionId>.log was hardcoded to 'debug', ignoring --log-level and DEBUG_MCP_LOG_LEVEL. The effective level now rides the init payload:

SessionManager (reads the winston logger's .level, which already resolves CLI flag → env → info) → ProxyConfig.logLevelProxyInitPayload.logLevelILoggerFactory(sessionId, logDir, level). Legacy parents that send no level keep the historical debug default, so old payloads behave identically.

Sibling noted while here (not fixed): adapter-registry.ts:397 also hardcodes logLevel: 'debug' when building per-adapter dependencies for dynamically loaded adapters — same bug class, no plumbing available there yet.

Deferred to #399's startup-janitor refactor

Sweeping stale per-session run dirs (proxy-*.log / dap-trace-*.ndjson): session logs live under os.tmpdir()/debug-mcp-server/sessions/<id>/run-<ts>/, out of reach of the flat logger.ts sweeper — a session-dir sweep fits naturally next to the orphan reapers being reworked in #399.

Docs

CLAUDE.md debugging scenarios + docs/development/setup-guide.md env table document DAP_TRACE / DAP_TRACE_FILE.

Tests (TDD, all watched fail first)

  • Worker: default factory off without DAP_TRACE, on with DAP_TRACE=1, explicit DAP_TRACE_FILE honored unrenamed; logLevel passed to the logger factory.
  • ProxyManager: logLevel included in the init command.
  • SessionManager: effective logger level lands in the proxy config (startCalls[0].logLevel).
  • Dependencies: loggerFactory passes the level to createLogger; legacy debug default without one.
  • MinimalDapClient: byte cap writes one truncation marker then stops.
  • Full npm test green (260 files / 3959 tests), npm run lint clean.

🤖 Generated with Claude Code

… log level

Two always-on observability defaults made every debug session pay for
diagnostics almost nobody reads (issue #403):

- Per-frame DAP tracing (synchronous appendFileSync of every frame, both
  directions, uncapped) is now opt-in: DAP_TRACE=1 enables the standard
  per-session dap-trace-<sessionId>.ndjson, an explicit DAP_TRACE_FILE is
  honored as-is, default is off. When enabled the trace is capped at 50 MB
  (one truncation marker, then silence) matching the main logger's maxsize.
  Sync writes are retained deliberately: tracing is now an opt-in crash-
  triage tool, and a buffered queue would lose the final frames exactly
  when the process dies.
- The per-session proxy-<sessionId>.log level is no longer hardcoded to
  'debug': the effective level (CLI --log-level / DEBUG_MCP_LOG_LEVEL)
  rides the init payload (ProxyConfig.logLevel -> ProxyInitPayload.logLevel
  -> ILoggerFactory level param). Legacy parents that send no level keep
  the historical debug default.

Stale session-run log sweeping is deferred to the #399 startup-janitor
refactor (session logs live under os.tmpdir()/debug-mcp-server/sessions,
out of reach of the flat logger sweeper).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit a5fb26f into main Aug 22, 2026
10 checks passed
@debugmcpdev
debugmcpdev deleted the perf/403-trace-gating-and-log-level branch August 22, 2026 02:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(observability): unconditional per-frame DAP trace (appendFileSync) + proxy logger hardcoded to debug

2 participants